package net.Group.MembershipService;

import net.jxta.credential.Credential;
import net.jxta.peergroup.PeerGroupID;
import net.jxta.peer.PeerID;
import net.jxta.id.ID;
import net.jxta.membership.MembershipService;
import net.jxta.document.Attributable;
import net.jxta.document.StructuredDocument;
import net.jxta.document.StructuredDocumentFactory;
import net.jxta.document.MimeMediaType;
import net.jxta.document.Element;
import net.jxta.impl.util.Base64;
import jxta.security.hash.Hash;
import jxta.security.impl.hash.MD5Hash;
import jxta.security.impl.publickey.RSAKey;
import jxta.security.impl.publickey.RSA;
import jxta.security.publickey.RSAPublickeyData;
import jxta.security.publickey.RSAPrivatekeyData;
import jxta.security.impl.cipher.KeyBuilder;
import jxta.security.exceptions.CryptoException;
import jxta.security.crypto.JxtaCrypto;
import jxta.security.impl.crypto.JxtaCryptoSuite;
import jxta.security.signature.Signature;
import jxta.security.util.URLBase64;

import java.text.DateFormat;

/*
 * This class represents a Credential that is supplied to a
 * peer after a successful apply process.The credential is 
 * signed using an RSA Signature 
 */
public class SecureCredential implements Credential {

    // Reference to the Membership Service
    private SecureMembershipService membershipService;
    private RSAKey rsaKey;
	private Signature signature;
	byte[] signatureArray;
	byte[] messageBytes;
	private boolean isExpired = false;
	private String message;
	private String role_id = null;
	private byte[] role_cert = null;
	private static final org.apache.log4j.Category LOG = 
        org.apache.log4j.Category.getInstance(SecureCredential.class.getName());
	private Base64 base64;
	
   /*
    * Constructor which takes a Membership Service Object
    */
    public SecureCredential(SecureMembershipService membershipService){
		this.membershipService = membershipService;   
		// We create a signed message
		createSignedMessage();
    }
   
    /*
     * Getter for the Membership Service
     */
    public MembershipService  getSourceService(){
        
        return membershipService;
    }

    /*
     * This method returns the PeerGroup ID
     */
    public ID getPeerGroupID(){
        return membershipService.getPeerGroup().getPeerGroupID();
    }
    
    /*
     * This method returns the Peer ID
     */
    public ID getPeerID(){
        return membershipService.getPeerGroup().getPeerID();
    }

    /*
     * This method returns a Structured Document representing the Credential
     */
    public StructuredDocument getDocument(MimeMediaType as) throws Exception {
        
            StructuredDocument doc =
                StructuredDocumentFactory.newStructuredDocument( as,"jxta:SecureCredential" );

            if (doc instanceof Attributable) {
                ((Attributable) doc).addAttribute("xmlns:jxta", "http://jxta.org");
                ((Attributable) doc).addAttribute("xml:space", "preserve");
                ((Attributable) doc).addAttribute("type", "jxta:LoTRBAC");
            }
           
            
            Element e = doc.createElement( "Message", message);
            doc.appendChild( e );
            
            e = doc.createElement("PeerID", new String(this.getPeerID().toString()));
            
            doc.appendChild( e );
            
            //String signature64 = new sun.misc.BASE64Encoder().encode(this.getMessageSignatureBytes());
            e = doc.createElement("Signature",base64.encodeBase64(this.getMessageSignatureBytes()));
            doc.appendChild( e );
            
            //??? Initially I wanted to put the Message signature into the structured 
            // document.But somehow it does not seem to work.I guess it has got something to
            // do with the encoding.Any ideas ?
            return doc;
    }

    private void createSignedMessage(){
    	String date    = DateFormat.getDateTimeInstance().format(new java.util.Date());
    	message = "Membership since "+date;
    	// Now we have created a Message that identifies the time 
    	// of Peer Membership Approval 
    	LOG.debug("Message is = "+message);
    	System.out.println("MESSAGE =>(Original)"+message);
		// Get the MD5 Hash of the message
        messageBytes = getMD5Hash(message);
        System.out.println("MESSAGE =>(MD5Hash)"+new String(messageBytes));
    	// Next step is to sign the message 
    	// For this we use the RSA Algorithm 
		try{
		
			rsaKey = (RSAKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA,
									             KeyBuilder.LENGTH_RSA_512,false);
			JxtaCrypto suite = new JxtaCryptoSuite(JxtaCrypto.PROFILE_RSA_SHA1,rsaKey, 
					        			Signature.ALG_RSA_SHA_PKCS1,(byte)0);
  			RSA rsaAlgorithm = new RSA(rsaKey);
			LOG.debug("Setting Public Key");
			// This method computes a public key
			rsaAlgorithm.setPublicKey();
			LOG.debug("Setting Private Key");
			// This method computes a private key			
			rsaAlgorithm.setPrivateKey();
			//Get the data for both public and private keys
			RSAPublickeyData publicKeyData =(RSAPublickeyData)rsaAlgorithm.getPublickey();
   	        RSAPrivatekeyData privateKeyData =(RSAPrivatekeyData)rsaAlgorithm.getPrivatekey();
   	        // Get a signature Object for the actual signing
   	        signature = suite.getJxtaSignature();
   	        signature.init(Signature.MODE_SIGN);
			signatureArray = signature.sign(messageBytes, 0, messageBytes.length);
		} catch (CryptoException cryptoException){
    		LOG.error("EXCEPTION IN SIGNING MESSAGE",cryptoException);
   		} catch (Exception exception){
    		LOG.error("EXCEPTION IN SIGNING MESSAGE",exception);    	
    	}
   	}

    public Signature getSignature(){
    	return signature;
   	}

    public byte[] getMessageBytes(){
    	return messageBytes;
   	}
    
    public byte[] getMessageSignatureBytes(){
    	return signatureArray;
   	}
    
	// Utility method to create an MD5 Hash
    private byte[] getMD5Hash(String string){
    	
   		  	// We can create a Hash Object
   		  	// It is not always necessary to use the Suite
    		Hash hash = new MD5Hash();
    		// Get the digest length of the Hash
       		int digestLength = hash.getDigestLength();
			byte[] passwordInBytes = string.getBytes();
			// Calculate the length needed by the output byte array
			int outputLength = (string.length() < digestLength ? digestLength : string.length());
			// Create a new array to hold the output
			byte[] outputBytes = new byte[digestLength]; 
			// This is where the actual hashing is done
			hash.doFinal(passwordInBytes, 0, passwordInBytes.length, outputBytes, 0);
			return outputBytes;

   	}
    
    public void setRoleID(String roleid)
    {
    	this.role_id = roleid;
    }
    
    public void setRoleCert(byte[] rolecert)
    {
    	this.role_cert = rolecert;
    }
    
	@Override
	public Object getSubject() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean isExpired() {
		// TODO Auto-generated method stub
		return isExpired;
	}

	@Override
	public boolean isValid() {
		// TODO Auto-generated method stub
		return false;
	}
}